package cn.wh.aop.aspectj;

import cn.wh.aop.ClassFilter;
import cn.wh.aop.MethodMatcher;
import cn.wh.aop.Pointcut;
import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

/**
 * @Description 切点表达式验证
 * @Version 1.0.0
 * @Date 2022/11/17 10:42
 * @Created wh
 */
public class AspectJExpressionPointcut  implements Pointcut, ClassFilter, MethodMatcher {
    private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>();


    static {
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
    }

    private final PointcutExpression pointcutExpression;

    public AspectJExpressionPointcut(String expression) {
        //返回一个切入点解析器，该解析器可以解析从AspectJ支持的切入点原语的用户定义子集构建的切入点表达式。
        // 适用的限制如下: 切入点指示符下面的if、cflow和cflow不受支持 切入点表达式必须是自包含的:—它们不能包含对其他已命名切入点的引用
        // 切入点表达式必须是匿名的，不允许使用任何形式。 在解析切入点表达式中的类型时，将使用给定的类加载器来查找类型。
        //可以使用PointcutParser为AspectJ的切入点语言的用户定义子集构建pointcutexpression
        PointcutParser pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.getClass().getClassLoader());
       //解析给定的切入点表达式。假定全局作用域用于解析任何类型引用，切入点必须不包含形式(要绑定的变量)。
        pointcutExpression=pointcutParser.parsePointcutExpression(expression);
    }

    @Override
    public boolean matches(Class<?> clazz) {
        //确定这个切入点是否能够匹配给定类中的连接点。如果这个切入点可能与(aClass)中的连接点匹配，则返回true，否则返回false
        return pointcutExpression.couldMatchJoinPointsInType(clazz);
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        //matchesMethodExecution() 确定这个切入点是否与给定方法的执行相匹配。正在执行的方法返回:一个ShadowMatch，指示切入点是否总是、有时或从不匹配表示方法执行的连接点。
        //alwaysMatches() 如果切入点表达式将匹配此影子上的任何连接点(例如，对给定方法的任何调用)，则为True。
        return pointcutExpression.matchesMethodExecution(method).alwaysMatches();
    }

    @Override
    public ClassFilter getClassFilter() {
        return this;
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return this;
    }
}
